package com.viknix.threadpool.manager.http.factory;

import com.viknix.threadpool.manager.http.TpmAsyncRestTemplate;
import com.viknix.threadpool.manager.http.TpmRestTemplate;
import com.viknix.threadpool.manager.http.SelfHostnameVerifier;
import com.viknix.threadpool.manager.http.TlsFileWatcher;
import com.viknix.threadpool.manager.http.config.HttpClientConfig;
import com.viknix.threadpool.manager.http.config.TlsSystemConfig;
import com.viknix.threadpool.manager.http.request.DefaultAsyncHttpClientRequest;
import com.viknix.threadpool.manager.http.request.JdkHttpClientRequest;
import com.viknix.threadpool.manager.http.tls.TlsHelper;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.impl.nio.client.HttpAsyncClients;
import org.apache.http.impl.nio.reactor.IOReactorConfig;
import org.apache.http.protocol.RequestContent;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.function.BiConsumer;

/**
 * @Author: Dongqi
 * @Date: 2021/11/22 10:56
 * @Version 1.0
 * @Description: AbstractHttpClientFactory Let the creator only specify the http client config.
 */
@Slf4j
public abstract class AbstractHttpClientFactory implements HttpClientFactory {

    @Override
    public TpmRestTemplate createTpmRestTemplate() {
        HttpClientConfig httpClientConfig = buildHttpClientConfig();
        final JdkHttpClientRequest clientRequest = new JdkHttpClientRequest(httpClientConfig);

        // enable ssl
        initTls(new BiConsumer<SSLContext, HostnameVerifier>() {
            @Override
            public void accept(SSLContext sslContext, HostnameVerifier hostnameVerifier) {
                clientRequest.setSSLContext(loadSSLContext());
                clientRequest.replaceSSLHostnameVerifier(hostnameVerifier);
            }
        }, new TlsFileWatcher.FileChangeListener() {
            @Override
            public void onChanged(String filePath) {
                clientRequest.setSSLContext(loadSSLContext());
            }
        });

        return new TpmRestTemplate(clientRequest);
    }

    @Override
    public TpmAsyncRestTemplate createTpmAsyncRestTemplate() {
        // 构建 http 客户端配置对象
        final HttpClientConfig originalRequestConfig = buildHttpClientConfig();
        return new TpmAsyncRestTemplate(new DefaultAsyncHttpClientRequest(
                HttpAsyncClients.custom()
                        .addInterceptorLast(new RequestContent(true))
                        .setDefaultIOReactorConfig(getIoReactorConfig())
                        .setDefaultRequestConfig(getRequestConfig())
                        .setMaxConnTotal(originalRequestConfig.getMaxConnTotal())
                        .setMaxConnPerRoute(originalRequestConfig.getMaxConnPerRoute())
                        .setUserAgent(originalRequestConfig.getUserAgent()).build()));
    }

    protected IOReactorConfig getIoReactorConfig() {
        HttpClientConfig httpClientConfig = buildHttpClientConfig();
        return IOReactorConfig.custom().setIoThreadCount(httpClientConfig.getIoThreadCount()).build();
    }

    protected RequestConfig getRequestConfig() {
        HttpClientConfig httpClientConfig = buildHttpClientConfig();
        return RequestConfig.custom().setConnectTimeout(httpClientConfig.getConTimeOutMillis())
                .setSocketTimeout(httpClientConfig.getReadTimeOutMillis())
                .setConnectionRequestTimeout(httpClientConfig.getConnectionRequestTimeout())
                .setContentCompressionEnabled(httpClientConfig.getContentCompressionEnabled())
                .setMaxRedirects(httpClientConfig.getMaxRedirects()).build();
    }

    protected void initTls(BiConsumer<SSLContext, HostnameVerifier> initTlsBiFunc,
                           TlsFileWatcher.FileChangeListener tlsChangeListener) {
        if (!TlsSystemConfig.tlsEnable) {
            return;
        }

        final HostnameVerifier hv = HttpsURLConnection.getDefaultHostnameVerifier();
        final SelfHostnameVerifier selfHostnameVerifier = new SelfHostnameVerifier(hv);

        initTlsBiFunc.accept(loadSSLContext(), selfHostnameVerifier);

        if (tlsChangeListener != null) {
            try {
                TlsFileWatcher.getInstance()
                        .addFileChangeListener(tlsChangeListener, TlsSystemConfig.tlsClientTrustCertPath,
                                TlsSystemConfig.tlsClientKeyPath);
            } catch (IOException e) {
                log.error("add tls file listener fail", e);
            }
        }
    }

    @SuppressWarnings("checkstyle:abbreviationaswordinname")
    protected synchronized SSLContext loadSSLContext() {
        if (!TlsSystemConfig.tlsEnable) {
            return null;
        }
        try {
            return TlsHelper.buildSslContext(true);
        } catch (NoSuchAlgorithmException e) {
            log.error("Failed to create SSLContext", e);
        } catch (KeyManagementException e) {
            log.error("Failed to create SSLContext", e);
        }
        return null;
    }

    /**
     * 构建 http 客户端配置。
     * 用于生产 RestTemplate
     *
     * @return HttpClientConfig
     */
    protected abstract HttpClientConfig buildHttpClientConfig();
}
